package lab_16.com.acme.bankapp.service.bank;

import java.io.IOException;
import java.util.*;

import lab_16.com.acme.bankapp.domain.bank.*;
import lab_16.com.acme.bankapp.domain.bank.Email.*;
import lab_16.com.acme.bankapp.domain.bank.Exceptions.*;
import lab_16.com.acme.bankapp.server.*;

public class BankApplication {

	final static String OUT_FEED_NAME = "out_feed.txt";
	final static String OUT_SERIALIZE_NAME = "out.srl";

	public static void main(String[] args) throws IOException, DataFileLoadingException {

		// Process command line arguments and read data
		Map<String, String> params = processArgs(args);

		Bank bank;

		if (params.containsKey("serializationFileName")) {
			// Load bank from serialization file
			bank = BankDataLoaderService.deserializeBankFromFile(params.get("serializationFileName"));
		} else if (params.containsKey("inputFeedFileName")) {
			// Creating a new bank
			bank = new Bank();
			bank.addListener(new Bank.Listeners.EmailNotificationListener());
			// bank.addListener(new Bank.Listeners.PrintClientListener());
			// Load bank from feed file
			BankDataLoaderService.updateBankFromFileFeed(bank, params.get("inputFeedFileName"));
		} else {
			// Read bank from hard-coded values
			bank = createBankWithHardcodedData();

		}

		// Start server
		if (params.containsKey("startServer")) {
			// start server here!!!
			System.out.println("Starting SEVER!!!");
			BankServer server = new BankServer(bank);
			server.run();

		}

		// Save all the bank data to the feed
		BankDataLoaderService.saveBankToFeed(bank, OUT_FEED_NAME);

		// Serialize bank to the file
		BankDataLoaderService.serializeBankToFile(bank, OUT_SERIALIZE_NAME);

		if (params.containsKey("statistics")) {
			System.out.println("Stats mode.");
			Dialogue dialogue = new Dialogue(bank);
			dialogue.run();
		}
	}

	/**
	 * Process command line arguments and read data from input file.
	 * 
	 * @param args
	 * @param bank
	 * @throws DataFileLoadingException
	 */
	private static Map<String, String> processArgs(String[] args) throws DataFileLoadingException {
		final Set<String> POSSIBLE_PARAMETERS = new HashSet<String>(Arrays.asList("-server",
				"-loadfeed", "-loadbank", "-statistics"));
		Map<String, String> params = new HashMap<String, String>();
		Set<String> keysFound = new HashSet<String>();
		for (int i = 0; i < args.length; i++) {
			if (!POSSIBLE_PARAMETERS.contains(args[i])) {
				System.out.printf("Unrecognized parameter: %s\n", args[i]);
				printHelp();
				System.exit(1);
			}

			if (args[i].equals("-loadfeed")) {
				if (keysFound.contains("-loadfeed")) {
					System.out.println("-loadfeed is provided several times.");
					System.exit(1);
				}
				if (i + 1 < args.length) {
					params.put("inputFeedFileName", args[i + 1]);
					keysFound.add("-loadfeed");
					i++; // skip the next parameter as we have already took the
							// input file name from it
					continue;
				} else {
					printHelp();
					throw new DataFileLoadingException("Filename for -loadfeed is not provided.");
				}
			}
			if (args[i].equals("-loadbank")) {
				if (keysFound.contains("-loadbank")) {
					System.out.println("-loadbank is provided several times.");
					System.exit(1);
				}
				if (i + 1 < args.length) {
					params.put("serializationFileName", args[i + 1]);
					keysFound.add("-loadbank");
					i++; // skip the next parameter as we have already took the
							// input file name from it
					continue;
				} else {
					printHelp();
					throw new DataFileLoadingException("Filename for -loadbank is not provided.");
				}
			}
			if (args[i].equals("-server")) {
				if (keysFound.contains("-server")) {
					System.out.println("-server is provided several times.");
					System.exit(1);
				}
				params.put("startServer", "true");
				keysFound.add("-server");
				continue;
			}

			if (args[i].equals("-statistics")) {
				if (keysFound.contains("-statistics")) {
					System.out.println("-statistics is provided several times.");
					System.exit(1);
				}
				params.put("statistics", "true");
				keysFound.add("-statistics");
				continue;
			}
		}

		// Test for redundant parameters provided
		List<String> redundantArgs = new LinkedList<String>();
		for (String arg : keysFound) {
			if (!POSSIBLE_PARAMETERS.contains(arg))
				redundantArgs.add(arg);
		}
		if (!redundantArgs.isEmpty()) {
			System.out.println("You have provided the following redundant keys:");
			System.out.println(redundantArgs);
			System.exit(1);
		}

		return params;
	}

	private static void printHelp() {
		System.out.println("Parameters:\n" + "-loadbank filename\n" + "   load bank from filename\n"
				+ "-server\n" + "   start server");
	}

	/**
	 * Add harcoded clients and accounts to a completely new bank to initialize
	 * it with some data.
	 * 
	 * @param bank
	 *            The new bank object
	 * @throws IOException
	 * @throws DataFileLoadingException
	 */
	@SuppressWarnings("unused")
	private static Bank createBankWithHardcodedData() throws IOException, DataFileLoadingException {

		Bank bank = new Bank();
		bank.addListener(new Bank.Listeners.EmailNotificationListener());
		// bank.addListener(new Bank.Listeners.PrintClientListener());

		System.out.println("Creating first client with savings account.");
		Client client1 = null;
		try {
			client1 = BankService.addClient(bank, new Client(Gender.FEMALE, "Lena", new SavingsAccount(
					10)));
		} catch (ClientExistsException e1) {
			System.out.println("A client with the same name already exists.");
		}
		// Info will be printed through invocation of PrintClientListener

		System.out.println("Adding second checking account.");
		try {
			client1.addClientAccount(new CheckingAccount(-5, 10));
		} catch (IllegalArgumentException e1) {
			System.out.println("Balance negative value shouldn't exceed the overdraft.");
		}
		// client1.printAllClientData();

		// Creating CheckingAccount with less money than overdraft limit
		try {
			client1.addClientAccount(new CheckingAccount(-10, 5));
		} catch (IllegalArgumentException e) {
			System.out.println("Balance negative value shouldn't exceed the overdraft.");
		}

		// Testing ClientExistsException
		try {
			System.out.println("Creating second client with the same name as the fist client");
			Client client2 = BankService.addClient(bank, new Client(Gender.FEMALE, "Lena",
					new CheckingAccount(5, 5)));
		} catch (ClientExistsException e) {
			System.out.println("A client with the same name already exists!");
		} catch (IllegalArgumentException e) {
			System.out.println("Balance negative value shouldn't exceed the overdraft.");
		}

		// Testing closing e-mail queue
		System.out.println("[main] Closing queue from main thread.");
		EmailService.close();

		// Add a new client
		try {
			System.out.println("[main] Creating third client.");
			Client client2 = BankService.addClient(bank, new Client(Gender.MALE, "Oleg",
					new CheckingAccount(15, 5)));
		} catch (ClientExistsException e) {
			System.out.println("A client with the same name already exists!");
		} catch (IllegalArgumentException e) {
			System.out.println("Balance negative value shouldn't exceed the overdraft.");
		}

		return bank;
	}

}
